package cn.gx.kevin.service.impl;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

import javax.annotation.Resource;

import cn.gx.kevin.common.tree.ListToTree;
import cn.gx.kevin.common.ui.ClientButton;
import cn.gx.kevin.common.user.ILoginUserContext;
import cn.gx.kevin.common.utils.*;
import cn.gx.kevin.dao.IOrgDao;
import cn.gx.kevin.domain.ImportOrg;
import cn.gx.kevin.domain.Org;
import cn.gx.kevin.service.IOrgService;
import cn.gx.kevin.dao.IBaseDao;
import cn.gx.kevin.xls.CellSytleUtils;
import cn.gx.kevin.xls.ColumnWidthStyleStrategy;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.github.pagehelper.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.multipart.MultipartFile;

/**
 * Generate by Kevin's CodeBuilder
 **/
@Service
public class OrgService extends BaseService<Org, String> implements IOrgService {

    private static final long serialVersionUID = -1L;


    @Autowired
    private ILoginUserContext loginUserCtx;

    IOrgDao orgDao;

    @Resource(name = "orgDao")
    @Override
    public void setDao(IBaseDao<Org, String> dao) {
        this.dao = dao;
        orgDao = (IOrgDao) dao;
    }

    private static ListToTree<Org> toTree;

    public OrgService() {
        toTree = new ListToTree<Org>(this);
    }


    @Override
    public Org selectParentOrg(String id) throws Exception {
        return orgDao.selectParentOrg(id);
    }

    @Override
    public int deleteBatch(List<String> list) throws Exception {
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("idList", list);
        List<Org> listOrg = this.selectParam(param);
        for (Org m : listOrg) {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("id", m.getId());
            params.put("pid", m.getPid());
            orgDao.updatePid(params);
        }
        return super.deleteBatch(list);
    }

    @Override
    public JSONArray getOrgJsonByPid(String pid, List<ClientButton> buttonlist) throws Exception {
        Map<String, Object> params = new HashMap<String, Object>();
        if(buttonlist == null){
            buttonlist = new ArrayList<>();
        }
        return this.getOrgJsonByPid(pid, buttonlist, params, 2);
    }

    /**
     * @param pid
     * @param buttonlist
     * @param params
     * @param deep       向下递归多少级数据
     * @return
     * @throws Exception
     */
    @Override
    public JSONArray getOrgJsonByPid(String pid, List<ClientButton> buttonlist, Map<String, Object> params, int deep) throws Exception {
        if (params == null) {
            params = new HashMap<String, Object>();
        }
        boolean queryAll = params.get("query_all") != null;
        params.put("pid", pid);
        List<Org> list = this.selectParam(params);
        List<Org> pOrgList = list;
        List<String> pidList = new ArrayList<String>();
        while (deep > 0) {
            pidList.clear();
            for (Org r : pOrgList) {
                if (r.getOrgType().equals(1)) {
                    pidList.add(r.getId());
                }
            }
            if (pidList.size() > 0) {
                params.clear();
                params.put("pidList", pidList);
                List<Org> childs = this.selectParam(params);
                if (childs.size() > 0) {
                    list.addAll(childs);
                    pOrgList = childs;
                } else {
                    break;
                }
            } else {
                break;
            }
            deep--;
        }

        if (queryAll && pOrgList.size() > 0) {
            pidList.clear();
            for (Org r : pOrgList) {
                if (r.getOrgType().equals(1)) {
                    pidList.add(r.getId());
                }
            }
            if (pidList.size() > 0) {
                this.queryAllChilds(list, pidList);
            }
        }
        toTree.setBtnList(buttonlist);
        boolean needTools = true;
        if (buttonlist.size() == 0) {
            needTools = false;
        }
        JSONArray res = new JSONArray();
        try {
            res = toTree.getJSONArray(pid, list, needTools);
        } catch (Throwable e) {
            logger.error("", e);
        }
        return res;
    }

    /***
     * 递归查询所有的子机构
     * ***/
    private void queryAllChilds(List<Org> resultList, List<String> pidList) throws Exception {
        Map params = new HashMap<String, Object>();
        params.put("pidList", pidList);
        if (this.count(params) > 0) {
            List<Org> childs = this.selectParam(params);
            resultList.addAll(childs);
            pidList.clear();
            for (Org r : childs) {
                if (r.getOrgType().equals(1)) {
                    pidList.add(r.getId());
                }
            }
            if (pidList.size() > 0) {
                this.queryAllChilds(resultList, pidList);
            }
        }
    }

    @Override
    public JSONArray getOrgJsonByPid(String pid) throws Exception {
        List<ClientButton> buttonlist = new ArrayList<ClientButton>();
        Map<String, Object> params = new HashMap<String, Object>();
        return this.getOrgJsonByPid(pid, buttonlist, params, 2);
    }

    @Override
    public JSONArray getOrgJsonByPid(String pid, Map<String, Object> params) throws Exception {
        List<ClientButton> buttonlist = new ArrayList<ClientButton>();
        return this.getOrgJsonByPid(pid, buttonlist, params, 2);
    }

    @Override
    public void createNode(Org bean, JSONObject obj, Object extParams) {

    }

    @Override
    public String getTreeId(Org bean, Object extParams) {
        // TODO Auto-generated method stub
        return bean.getId();
    }

    @Override
    public String getTreeText(Org bean, Object extParams) {
        // TODO Auto-generated method stub
        return bean.getOrgName();
    }

    @Override
    public String getTreePid(Org bean, Object extParams) {
        // TODO Auto-generated method stub
        return  bean.getPid();
    }

    @Override
    public boolean isTreeLeaf(Org bean, Object extParams) {
        // TODO Auto-generated method stub
        return bean.getOrgType().equals(0);
    }

    @Override
    public ClientButton createTreeButton(ClientButton btn, Org bean, Object extParams) {
        if (bean.getOrgType().equals(0) && ("lineMrgFn".equals(btn.getHandler()) || "addChildFn".equals(btn.getHandler()))) { //没有下属机构的
            return null;
        }
        return btn;
    }

    private DynamicParameter getExpParams(int pageSize){
        DynamicParameter dynamicParameter = new DynamicParameter();
        LinkedHashMap<String,String> orderMap = new LinkedHashMap<>();
        orderMap.put("order_index","asc");
        orderMap.put("id","asc");
        orderMap.put("pid","asc");
        dynamicParameter.setOrderMap(orderMap);
        dynamicParameter.setSize(pageSize);
        return dynamicParameter;
    }


    private Map<String,Org> getParentOrgCodeMap( List<String> idList) throws Exception{
        Map<String,Object> params = new HashMap<>();
        List<String> selectFields = new ArrayList<>();
        selectFields.add("id");
        selectFields.add("org_code as orgCode");
        params.put("selectFields",selectFields);
        params.put("idList",idList);
        Map<String,Org> idCodeMap =  this.orgDao.selectMap(params,"id");
        return idCodeMap;
    }

    //分页导出数据
    @Override
    public void exportOrgs(OutputStream outputStream,Map<String,Object> queryParams) throws Exception {
        int page = 1;
        long pageCount = 1;
        int pageSize = 999;
        DynamicParameter dynamicParameter = getExpParams(pageSize);
        dynamicParameter.setParams(queryParams);
        ExcelWriter excelWriter = null;
        try{
            List<String> pidList = new ArrayList<>();
            HorizontalCellStyleStrategy styleStrategy = CellSytleUtils.getDefaultStyle();
            excelWriter = EasyExcel.write(outputStream, Org.class).build();
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("机构列表").registerWriteHandler(styleStrategy).registerWriteHandler(new ColumnWidthStyleStrategy()).build();
            while(page <= pageCount){
                dynamicParameter.setPage(page);
                PagingResult<Org> result = this.selectPagination(dynamicParameter);
                List<Org> list = result.getResultList();
                pageCount = result.getPageCount(pageSize);
                if(list.size() == 0){
                    break;
                }
                String id = String.valueOf( queryParams.get("expid"));
                List<String> allPidList = new ArrayList<>();
                for(Org org : list){
                    if(!org.getId().equals(id) && org.getOrgType().equals(1)){
                        pidList.add(org.getId());
                    }
                    allPidList.add(org.getPid());
                }
                //补充上prtOrgCode 父机构代码
                Map<String,Org> idCodeMap = getParentOrgCodeMap(allPidList);
                for(Org org : list){
                    Org codeOrg = idCodeMap.get(org.getPid());
                    if(codeOrg != null){
                        org.setPrtOrgCode(codeOrg.getOrgCode());
                    }
                }
                excelWriter.write(list, writeSheet);
                page++;
            }
            if(queryParams.size() > 1 && pidList.size() > 0){ //是按调整查询的
                expChilds(pidList,excelWriter,writeSheet);
            }
        }catch (Exception x){
            throw x;
        }finally {
            if(excelWriter != null){
                try{
                    excelWriter.finish();
                }catch (Exception x){
                }
            }
        }
    }

    void expChilds(List<String> pidList,  ExcelWriter excelWriter, WriteSheet writeSheet) throws Exception{
        Map<String,Object> params = new HashMap<>();
        params.put("pidList",pidList);
        int page = 1;
        long pageCount = 1;
        int pageSize = 999;
        DynamicParameter dynamicParameter = getExpParams(pageSize);
        dynamicParameter.setParams(params);
        List<String> nextPidList = new ArrayList<>();
        while(page <= pageCount){
            dynamicParameter.setPage(page);
            PagingResult<Org> result = this.selectPagination(dynamicParameter);
            List<Org> list = result.getResultList();
            pageCount = result.getPageCount(pageSize);
            if(list.size() == 0){
                break;
            }
            List<String> allPidList = new ArrayList<>();
            for(Org org : list){
                if(org.getOrgType().equals(1)){
                    nextPidList.add(org.getId());
                }
                allPidList.add(org.getPid());
            }
            //补充上prtOrgCode 父机构代码
            Map<String,Org> idCodeMap = getParentOrgCodeMap(allPidList);
            for(Org org : list){
                Org codeOrg = idCodeMap.get(org.getPid());
                if(codeOrg != null){
                    org.setPrtOrgCode(codeOrg.getOrgCode());
                }
            }
            excelWriter.write(list, writeSheet);
            page++;
        }
        if(nextPidList.size() > 0){ //是按调整查询的
            expChilds(nextPidList,excelWriter,writeSheet);
        }
    }

    @Override
    public long importOrgs(MultipartFile xlsFile,  String rootCode,String pid) throws Exception {
        long count = 0;
        Org prtOrg = null;
        boolean isFull = StringUtils.isEmpty(pid);
        if (isFull) {
            Map<String, Object> params = new HashMap<>();
            params.put(SysConstants.forceExeSqlParamKey, true);
            this.orgDao.deleteParam(params);
        }else{
            this.deleteChilds(pid);
            prtOrg = this.orgDao.get(pid);
        }
        final  Org prterOrg = prtOrg;
        InputStream inputStream = null;
        Date now = new Date();
        String userId = loginUserCtx.getCurrentUser().getId();
        final List<ImportOrg> rootOrgList = new ArrayList<>();
        Set<String> rootCodeSet = new HashSet<>();
        Set<String> codeFilter = new HashSet<>();//过滤重复数据
        if (rootCode.contains(";")) {
            String[] tmps = rootCode.split(";");
            for (String s : tmps) {
                rootCodeSet.add(s);
            }
        } else {
            rootCodeSet.add(rootCode);
        }
        try {
            Map<String, List<Org>> orgTreeMap = new HashMap<>();
            inputStream = xlsFile.getInputStream();
            EasyExcel.read(inputStream, new AnalysisEventListener<LinkedHashMap>() {
                @Override
                public void invoke(LinkedHashMap o, AnalysisContext analysisContext) {
                    String orgCode = "";
                    String orgParentCode = "";
                    String orgName = "";
                    String orgAddr = "";
                    Object obj = o.get(0);
                    if (obj != null) {
                        orgCode = String.valueOf(obj);
                    }
                    if (!orgCode.equals("")) {
                        obj = o.get(1);
                        if (obj != null) {
                            orgParentCode = String.valueOf(obj);
                        }
                        obj = o.get(2);
                        if (obj != null) {
                            orgName = String.valueOf(obj);
                        }
                        obj = o.get(3);
                        if (obj != null) {
                            orgAddr = String.valueOf(obj);
                        }
                        if (!codeFilter.contains(orgCode)) {
                            codeFilter.add(orgCode);
                            Org org = new Org();
                            org.setCreateTime(now);
                            org.setCreateUserId(userId);
                            org.setOrderIndex(1);
                            org.setOrgAddr(orgAddr);
                            org.setOrgName(orgName);
                            org.setOrgCode(orgCode);
                            org.setStatus(1);
                            //根元素先收集起来
                            if (rootCodeSet.contains(orgCode)) {
                                org.setId(MyUUID.snowflakeId());
                                if(prterOrg != null){
                                    org.setPid(prterOrg.getId());
                                }else{
                                    org.setPid("0");
                                }
                                ImportOrg importOrg = new ImportOrg();
                                importOrg.org = org;
                                importOrg.parentOrgCode = orgParentCode;
                                rootOrgList.add(importOrg);
                            } else {
                                if (StringUtils.isNotEmpty(orgParentCode)) {
                                    List<Org> childs = orgTreeMap.get(orgParentCode);
                                    if (childs == null) {
                                        childs = new ArrayList<>();
                                        orgTreeMap.put(orgParentCode, childs);
                                    }
                                    childs.add(org);
                                }
                            }
                        }
                    }
                }
                @Override
                public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                }
            }).sheet(0).doRead();
            inputStream.close();
            inputStream = null;
            if (rootOrgList.size() == 0) {
                throw new Exception("the root org is not found :" + rootCode + "");
            }
            List<Org> parentNewOrgs = new ArrayList<>();
            if (isFull) {
                for (ImportOrg importOrg : rootOrgList) {
                    parentNewOrgs.add(importOrg.org);
                    if (orgTreeMap.containsKey(importOrg.org.getOrgCode())) {
                        importOrg.org.setOrgType(1);
                    } else {
                        importOrg.org.setOrgType(0);
                    }
                }
            } else {
                List<String> orgCodes = new ArrayList<>();
                for (ImportOrg importOrg : rootOrgList) {
                    if (StringUtils.isNotEmpty(importOrg.parentOrgCode)) {
                        orgCodes.add(importOrg.parentOrgCode);
                    }
                    if (orgTreeMap.containsKey(importOrg.org.getOrgCode())) {
                        importOrg.org.setOrgType(1);
                    } else {
                        importOrg.org.setOrgType(0);
                    }
                }
                if (orgCodes.size() > 0) {
                    Map<String, Object> parsms = new HashMap<>();
                    parsms.put("orgCodes", orgCodes);
                    List<Org> parentOrg = this.orgDao.selectParam(parsms);
                    for (Org org : parentOrg) {
                        for (ImportOrg importOrg : rootOrgList) {
                            if (org.getOrgCode().equals(importOrg.parentOrgCode)) {
                                importOrg.org.setPid(org.getId());
                                parentNewOrgs.add(importOrg.org);
                                break;
                            }
                        }
                    }
                }
            }
            if (parentNewOrgs.size() == 0) {
                throw new Exception("the root org is not found, parentNewOrgs is Empty !" + rootCode + "");
            }
            List<Org> newOrgs = new ArrayList<>();
            count = this.orgDao.insertBatch(parentNewOrgs);//先批量插入父节点
            for (Org pOrg : parentNewOrgs) {
                long tp = this.insertChilds(pOrg, orgTreeMap, newOrgs);
                count = count + tp;
            }
            if (newOrgs.size() > 0) {
                long r = this.orgDao.insertBatch(newOrgs);//批量插节点
                count = count + r;
            }
        } catch (Exception x) {
            throw x;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception x) {
            }
        }
        return count;
    }

    private long insertChilds(Org parentOrg, Map<String, List<Org>> treeOrgMap, List<Org> newOrgs) throws Exception {
        long r = 0;
        if (newOrgs.size() > 400) {
            r = this.orgDao.insertBatch(newOrgs);//批量插节点
            newOrgs.clear();
        }
        String pOrgCode = parentOrg.getOrgCode();
        List<Org> childs = treeOrgMap.get(pOrgCode);
        if (childs != null) {
            String pid = parentOrg.getId();
            for (Org child : childs) {
                child.setPid(pid);
                child.setId(MyUUID.snowflakeId());
                boolean isParent = false;
                if (treeOrgMap.containsKey(child.getOrgCode())) {
                    child.setOrgType(1);
                    isParent = true;
                } else {
                    child.setOrgType(0);
                }
                if (isParent) {
                    long c = this.insertChilds(child, treeOrgMap, newOrgs);
                    r = r + c;
                }
            }
            newOrgs.addAll(childs);
        }
        return r;
    }


    @Override
    public void deleteOrg(String id, boolean delSons) throws Exception {
        if(delSons){
            this.deleteChilds(id);
        }else{
            List<Org> childs =  this.selectOnfield(getDiyQueryParams(id));
            List<String> idList = new ArrayList<>();
            for(Org org : childs){
                idList.add(org.getId());
            }
            if(idList.size() > 0){
                Org pOrg =  this.get(id);
                Map<String, Object> params = new HashMap<>();
                params.put("idList",idList);
                params.put("pid",pOrg.getPid());
                this.orgDao.updatePid(params);
            }
        }
        this.delete(id);
    }

    private  Map<String,Object> getDiyQueryParams(String pid){
        Map<String,Object> params = new HashMap<>();
        List<String> selectFields = new ArrayList<>();
        List<WhereField> whereLIst = new LinkedList<>();
        selectFields.add("id");
        selectFields.add("org_type as orgType");
        params.put("selectFields",selectFields);
        WhereField whereFields = new WhereField("", "pid", "=", pid);
        whereLIst.add(whereFields);
        params.put("whereFields",whereLIst);
        return params;
    }

    private void deleteChilds(String pid)  throws Exception{
        List<Org> childs =  this.selectOnfield(getDiyQueryParams(pid));
        if(childs.size() > 0){
            List<String> ids = new ArrayList<>();
            for(Org org : childs){
                String id = org.getId();
                if(org.getOrgType().equals(1)){
                    this.deleteChilds(id);
                }
                ids.add(id);
            }
            if(ids.size() > 0){
                this.deleteBatch(ids);
            }
        }
    }

    @Override
    public List<List<String>> getAllChildsIdByPid(String pid) throws Exception {
        List<String> pidList = new ArrayList<>();
        pidList.add(pid);
        List<List<String>> idArrays = new ArrayList<>();
        loadChildsByPid(pidList,idArrays);
        return idArrays;
    }

    private void loadChildsByPid( List<String> pidList , List<List<String>> idRestults) throws Exception {
        List<String> idList;
        if(idRestults.size() == 0){
            idList = new ArrayList<>();
            idRestults.add(idList);
        }else{
            idList = idRestults.get(idRestults.size() -1);
            if(idList.size() > 900){
                idList = new ArrayList<>();
                idRestults.add(idList);
            }
        }
        List<Org> orgs =  this.orgDao.selectChildsByPid(pidList);
        List<String> nextPidList = new ArrayList<>();
        for(Org org : orgs){
            if(org.getOrgType().equals(1)){
                nextPidList.add(org.getId());
            }
            if(idList.size() > 900){
                idList = new ArrayList<>();
                idRestults.add(idList);
            }
            idList.add(org.getId());
        }
        if(nextPidList.size() > 0){
            loadChildsByPid( nextPidList ,  idRestults);
        }
    }

    @Override
    public List<Org> getRootOrgs() throws Exception {
        Map<String,Object> prs = new HashMap<>();
        prs.put("pid",0);
        return this.orgDao.selectParam(prs);
    }

    @Override
    public void updateOrgCode() throws Exception{
        List<Org> orgs = this.getRootOrgs();
        this.updateCodes(1,orgs);
    }
    private void updateCodes(int code,List<Org> orgs) throws Exception{
        for(Org org : orgs){
            String name = org.getOrgName();
            if(name.contains("/")){
                name = name.replaceAll("/","_");
                org.setOrgName(name);
            }
            org.setOrgCode(String.valueOf(code));
            this.update(org);

            Map<String,Object> params = new HashMap<>();
            params.put("pid",org.getId());
            List<Org> childs = this.selectParam(params);
            this.updateCodes(code * 10000,childs);
            code++;
        }
    }
}
